#include <iostream>
#include <memory>
#include <unordered_map>

class Test {
public:
    Test() { std::cout << "Test::Test()" << std::endl; }
    ~Test() { std::cout << "Test::~Test()" << std::endl; }
};

class Cls {
    int a;
public:
    Cls(int a): a(a) { std::cout << "Cls::Cls(int a), a: " << a << std::endl; }
    ~Cls() {  std::cout << "Cls::~Cls(), a: " << a << std::endl;  }
};

std::unique_ptr<const Cls> LoadCls(int a) {
    //do sth expensive...
    return std::unique_ptr<const Cls>(new Cls(a));
}

std::shared_ptr<const Cls> LoadClsShared(int a) {
    //todo ...
    return std::shared_ptr<const Cls>(new Cls(a));
}

std::shared_ptr<const Cls> FastLoadCls(int a) {
    static std::unordered_map<int, 
        std::weak_ptr<const Cls>> cache;

    std::shared_ptr<const Cls> cls;

    if (cache.find(a) == cache.end()) {
        //不能这么写, 这样写返回的临时 shared_ptr 会被析构, cache[a] 直接无效
        //cache[a] = LoadClsShared(a);

        cls = LoadClsShared(a);
        cache[a] = cls;

        return cache[a].lock();
    }

    cls = cache[a].lock();
    if (cls)
        return cls;

    cls = LoadClsShared(a);
    cache[a] = cls;
    return cache[a].lock();
}

class A {
public:
    A() { std::cout << "A::A()" << std::endl; }
    ~A() { std::cout << "A::~A()" << std::endl; }
public: 
    std::shared_ptr<class B> Pb;
};

class B {
public:
    B() { std::cout << "B::B()" << std::endl; }
    ~B() { std::cout << "B::~B()" << std::endl; }
public: // what ptr for class A / C ?
    //std::shared_ptr<A> Pa;

    std::weak_ptr<A> Pa;
};

class C {
public:
    C() { std::cout << "C::C()" << std::endl; }
    ~C() { std::cout << "C::~C()" << std::endl; }
public: 
    std::shared_ptr<class B> Pb;
};

int main() {
    {
        std::shared_ptr<Test> p(new Test);
        std::weak_ptr<Test> pw(p);

        if (pw.expired())
            std::cout << "pw.expired() --- 1" << std::endl;

        p = nullptr;
        if (pw.expired())
            std::cout << "pw.expired() --- 2" << std::endl;

        auto p1 = pw.lock();

        try {
            std::shared_ptr<Test> p2(pw);
        }
        catch (std::bad_weak_ptr& e) {
            std::cout << e.what() << std::endl;
        }
    }

    {
        auto cls0 = FastLoadCls(0);
        auto cls0_1 = FastLoadCls(0);

        auto cls1 = FastLoadCls(1);
    }

    {
        auto cls0 = FastLoadCls(0);
        auto cls1 = FastLoadCls(1);
    }

    {
        auto b = std::shared_ptr<B>(new B);

        auto a = std::shared_ptr<A>(new A);
        a->Pb = b;

        b->Pa = a;

        auto c = std::shared_ptr<C>(new C);
        c->Pb = b;
    }

    return 0;
}